package com.z.platform.controller.persistence.orm;

import com.google.common.collect.Maps;
import com.z.platform.controller.persistence.orm.annotation.Column;
import com.z.platform.controller.persistence.orm.annotation.Id;
import com.z.platform.controller.persistence.orm.annotation.QBindAttrField;
import com.z.platform.controller.persistence.orm.annotation.QBindEntity;
import com.z.platform.controller.persistence.orm.metadataModel.*;
import com.z.ssmdemo.log.factory.ELogger;
import com.z.ssmdemo.log.factory.LogFactory;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 21051
 */
public class SqlAnalyzer {
    private static final ELogger LOG = LogFactory.getLog(SqlAnalyzer.class);
    private static final String INSERT = "INSERT";
    private static final String UPDATE = "UPDATE";
    private static final String DELETE = "DELETE";
    private static Object SplicingParams;

    public static Map<Class<?>, SqlAnalyzer> sqlMaps = Maps.newConcurrentMap();

    public static SqlMetadata analyzeFilterChain(TableMetadata tableMetadata, Entity entity, BaseQuery baseQuery) throws IllegalAccessException, IntrospectionException, InvocationTargetException {
        if (Objects.isNull(tableMetadata) || Objects.isNull(entity)) {
            throw new RuntimeException("实体为空或者传入的实体没有需要的配置！");
        }
        SqlMetadata sqlMetadata = new SqlMetadata();

        //封装和插入语句相关的sql和参数以及类型
        splicingInsertAndDeleteSql(tableMetadata, entity, sqlMetadata, (item) -> {
            StringJoiner columnKeys = new StringJoiner(", ", "(", ")");
            StringJoiner placeholder = new StringJoiner(", ", "(", ")");
            item.getFiledSplicing().forEach((key, value) -> {
                columnKeys.add(value);
            });
            item.getPlaceholderSplicing().forEach((key, value) -> {
                placeholder.add(value);
            });
            return " INSERT INTO `java_demo`." + "`" + item.getTableName() + "`" + columnKeys.toString() +
                    " VALUES " + placeholder.toString();
        });
        //封装删除语句相关的sql和参数以及类型
        splicingInsertAndDeleteSql(tableMetadata, entity, sqlMetadata, (item) -> {
            StringBuilder stringBuilder = new StringBuilder().append(" DELETE FROM `java_demo`." + "`").append(item.getTableName()).append("` WHERE");
            item.getFiledSplicing().forEach((key, value) -> {
                stringBuilder.append(value);
                stringBuilder.append(" = ");
                stringBuilder.append(item.getPlaceholderSplicing().get(key));
                if (item.getFiledSplicing().size() > key) {
                    stringBuilder.append(" and ");
                }
            });
            return stringBuilder.toString();
        });
        //封装更新语句相关的sql和参数以及类型
        splicingSelectAndUpdateSql(baseQuery, sqlMetadata, (item) -> {
            //UPDATE `java_demo`.`DEMO_USER` SET `pazz_word` = '123456d777' WHERE `id` = 'c2a741bd-6268-4094-a962-0f759d840546'
            StringBuilder stringBuilder = new StringBuilder().append(" UPDATE FROM " + "`").append(item.getTableName()).append("` SET ");
            item.getFiledSplicing().forEach((key, value) -> {
                stringBuilder.append(value);
                stringBuilder.append(" = ");
                stringBuilder.append(item.getPlaceholderSplicing().get(key));
                if (item.getFiledSplicing().size() > key) {
                    stringBuilder.append(",");
                }
            });
            ConcurrentMap<Integer, String> wheres = item.getWheres();
            if (!wheres.isEmpty()) {
                stringBuilder.append(" WHERE ");
                wheres.forEach((key, value) -> {
                    stringBuilder.append(value);
                    switch (item.getWheresCondition().get(key)) {
                        case equal:
                            //todo
                            break;
                        case greaterThanOrEqualTo:
                            //todo
                            break;
                        case lessThanOrEqualTo:
                            //todo
                            break;
                        case like:
                            //todo
                            break;
                        case greaterThan:
                            //todo
                            break;
                        case lessThan:
                            //todo
                            break;
                        case notEqual:
                            //todo
                            break;
                        case in:
                            //Collection
//                            if (obj instanceof Collection ){
//                                List<?> value = (List<?>) obj;
//                                if (value.size() == 0) {
//                                    // 防止生成LIST时，没有传入值，而查询条件会做全查处理，此处做特殊处理返回空条件
//                                    ((List<?>) obj).add(null);
//                                }
//                                if (value.size() > 20) {
//                                    Set<Object> set = new HashSet<Object>(
//                                            value.size());
//                                    // 如果in超过20个要去重处理
//                                    set.addAll(value);
//                                    value = new ArrayList<Object>(set);
//                                }
//                                predicatesAnd.add(expression.in(value)) ;
//                            }
                            //特殊处理
                            break;
                        default://默认等于equal
                            break;
                    }
                });
            }
            return stringBuilder.toString();
        });
        return sqlMetadata;
    }

    private static void splicingInsertAndDeleteSql(TableMetadata tableMetadata, Entity entity, SqlMetadata sqlMetadata, Function<SplicingParams<ConcurrentMap<Integer, String>>, String> supplier) {
        String tableName = tableMetadata.getTableName();
        Class<? extends Entity> entityClass = entity.getClass();
        ColumnMetadata primaryColumnMetadata = tableMetadata.getPrimaryKey();
        List<ColumnMetadata> allColumnMetadata = tableMetadata.getAllColumnMetadata();
        //属性的值
        ConcurrentMap<Integer, Object> valueConcurrentMap = Maps.newConcurrentMap();
        //属性的类型
        ConcurrentMap<Integer, Class<?>> typeConcurrentMap = Maps.newConcurrentMap();
        //存在的key也就是数据库中的列名
        ConcurrentMap<Integer, String> columnKeys = Maps.newConcurrentMap();
        //和key对应个数的占位符
        ConcurrentMap<Integer, String> placeholder = Maps.newConcurrentMap();
        //占位符的key
        AtomicInteger index = new AtomicInteger(0);

        try {
            String primaryKeyFieldName = tableMetadata.getPrimaryKey().getFieldName();
            Field field = entityClass.getField(primaryKeyFieldName);
            typeConcurrentMap.put(index.incrementAndGet(), field.getType());
            valueConcurrentMap.put(index.get(), field.get(entity));
            columnKeys.put(index.get(), primaryColumnMetadata.getColumnName());
            placeholder.put(index.get(), "?");
        } catch (IllegalAccessException | NoSuchFieldException e) {
            e.printStackTrace();
        }
        if (Objects.nonNull(allColumnMetadata)) {
            //其他字段拼接
            allColumnMetadata.forEach(notNullItemField -> {
                String fieldName = notNullItemField.getFieldName();
                Field ordinaryField = null;
                try {
                    ordinaryField = entityClass.getField(fieldName);
                    typeConcurrentMap.put(index.incrementAndGet(), ordinaryField.getType());
                    valueConcurrentMap.put(index.get(), ordinaryField.get(entity));
                    columnKeys.put(index.get(), notNullItemField.getColumnName());
                    placeholder.put(index.get(), "?");
                } catch (IllegalAccessException | NoSuchFieldException e) {
                    e.printStackTrace();
                }
            });
        }

        SplicingParams<ConcurrentMap<Integer, String>> splicingParams = new SplicingParams<ConcurrentMap<Integer, String>>();
        splicingParams.setTableName(tableName);
        splicingParams.setFiledSplicing(columnKeys);
        splicingParams.setPlaceholderSplicing(placeholder);
        //拼接sql
        String sql = supplier.apply(splicingParams);
        //记录对应属性的值
        if (StringUtils.isBlank(sql)) {
            throw new RuntimeException("值都没给，干嘛呢？");
        }
        if (sql.contains(INSERT.toLowerCase()) || sql.contains(INSERT)) {
            sqlMetadata.setInsertSqlParams(valueConcurrentMap);
            //记录对应属性类型
            sqlMetadata.setInsertSqlParamsTypes(typeConcurrentMap);
            //记录sql
            sqlMetadata.setInsertSql(sql);
        } else if (sql.contains(DELETE.toLowerCase()) || sql.contains(DELETE)) {
            sqlMetadata.setDeleteByIdSqlParams(valueConcurrentMap);
            //记录对应属性类型
            sqlMetadata.setDeleteByIdSqlParamsTypes(typeConcurrentMap);
            //记录sql
            sqlMetadata.setDeleteByIdSql(sql);
        }

        LOG.info("sql语句是{}", sql);
    }

    private static void splicingSelectAndUpdateSql(BaseQuery baseQuery, SqlMetadata sqlMetadata, Function<SplicingParams<ConcurrentMap<Integer, String>>, String> supplier) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        Class<? extends BaseQuery> entityClass = baseQuery.getClass();
        QBindEntity qBindEntity = entityClass.getAnnotation(QBindEntity.class);
        if (Objects.nonNull(qBindEntity)) {
            Class<?> aClass = qBindEntity.entityClass();
            TableMetadata metadata = TableAnalyzer.tableMaps.get(aClass);
            if (Objects.isNull(TableAnalyzer.tableMaps.get(aClass))) {
                throw new RuntimeException("不存在该表元数据对象！");
            }

            List<ColumnMetadata> allColumnMetadata = metadata.getAllColumnMetadata();
            //属性的值
            ConcurrentMap<Integer, Object> valueConcurrentMap = Maps.newConcurrentMap();
            //属性的类型
            ConcurrentMap<Integer, Class<?>> typeConcurrentMap = Maps.newConcurrentMap();
            //存在的key也就是数据库中的列名
            ConcurrentMap<Integer, String> columnKeys = Maps.newConcurrentMap();
            //和key对应个数的占位符
            ConcurrentMap<Integer, String> placeholder = Maps.newConcurrentMap();
            ConcurrentMap<Integer, String> wheresPlaceholder = Maps.newConcurrentMap();
            //占位符的key
            AtomicInteger index = new AtomicInteger(0);
            AtomicInteger whereIndex = new AtomicInteger(0);
            //where条件属性
            ConcurrentMap<Integer, String> wheres = Maps.newConcurrentMap();
            //更新或者查询条件
            ConcurrentMap<Integer, Where> wheresCondition = Maps.newConcurrentMap();

            HashMap<@Nullable String, @Nullable Object> wheresReadMethodMap = getReadMethodMap(baseQuery);

            List<Field> fieldList = Arrays.stream(aClass.getDeclaredFields()).filter(item -> {
                return Objects.nonNull(item.getAnnotation(QBindAttrField.class));
            }).collect(Collectors.toList());

            if (Objects.isNull(allColumnMetadata) || fieldList.isEmpty()) {
                throw new RuntimeException("查询和更新语句中的baseQuery和Entity不能为空！");
            }
            //其他字段拼接
            allColumnMetadata.forEach(notNullItemField -> {
                String fieldName = notNullItemField.getFieldName();
                Field ordinaryField = null;
                try {
                    ordinaryField = entityClass.getField(fieldName);
                    typeConcurrentMap.put(index.incrementAndGet(), ordinaryField.getType());
                    //表实体对应值
                    valueConcurrentMap.put(index.get(), null);
                    columnKeys.put(index.get(), notNullItemField.getColumnName());
                    placeholder.put(index.get(), "?");
                } catch ( NoSuchFieldException e) {
                    e.printStackTrace();
                }
            });
            fieldList.forEach(item -> {
                typeConcurrentMap.put(index.incrementAndGet(), item.getType());
                valueConcurrentMap.put(index.get(), wheresReadMethodMap.get(item.getName()));
                wheres.put(whereIndex.incrementAndGet(), item.getAnnotation(QBindAttrField.class).fieldName());
                wheresCondition.put(whereIndex.get(), item.getAnnotation(QBindAttrField.class).where());
                wheresPlaceholder.put(whereIndex.get(), "?");
            });


            SplicingParams<ConcurrentMap<Integer, String>> splicingParams = new SplicingParams<ConcurrentMap<Integer, String>>();
            splicingParams.setTableName(metadata.getTableName());
            splicingParams.setFiledSplicing(columnKeys);
            splicingParams.setPlaceholderSplicing(placeholder);
            splicingParams.setWheres(wheres);
            splicingParams.setWheresCondition(wheresCondition);
            splicingParams.setWheresPlaceholder(wheresPlaceholder);
            //拼接sql
            String sql = supplier.apply(splicingParams);

            //记录对应属性的值
            if (StringUtils.isBlank(sql)) {
                throw new RuntimeException("值都没给，干嘛呢？");
            }
            if (sql.contains(UPDATE.toLowerCase()) || sql.contains(UPDATE)) {
                sqlMetadata.setUpdateByIdSqlParams(valueConcurrentMap);
                //记录对应属性类型
                sqlMetadata.setUpdateByIdSqlParamsTypes(typeConcurrentMap);
                //记录sql
                sqlMetadata.setUpdateByIdSql(sql);
            }
            LOG.info("sql语句是{}", sql);
        }

    }

    private static HashMap<@Nullable String, @Nullable Object> getReadMethodMap(Object baseQuery) throws IntrospectionException, IllegalAccessException, InvocationTargetException {
        BeanInfo beanInfo = Introspector.getBeanInfo(baseQuery.getClass());
        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
        HashMap<@Nullable String, @Nullable Object> readMethodMap = Maps.newHashMap();
        for (PropertyDescriptor pd : pds) {
            Method readMethod = pd.getReadMethod();
            String name = pd.getName();
            if ((name.indexOf("Page") == 0)
                    || (pd.getName().indexOf("Sort") == 0)) {
                continue;
            }
            if (!"class".equals(name)) {
                Object obj = readMethod.invoke(baseQuery);
                if (obj != null) {
                    Annotation fieldProp = getBindFieldName(baseQuery, pd.getName());
                    if (fieldProp == null) {
                        continue;
                    }
                    readMethodMap.put(name, obj);
                }
            }
        }
        return readMethodMap;
    }

    /**
     * 获取绑定字段属性值
     *
     * @param PropertyName
     * @return
     */
    public static Annotation getBindFieldName(Object query, String PropertyName) {
        try {
            Field field = query.getClass().getDeclaredField(PropertyName);
            Annotation anno = field.getAnnotation(QBindAttrField.class);
            Annotation anno1 = field.getAnnotation(Column.class);
            Annotation anno2 = field.getAnnotation(Id.class);

            if (anno != null || anno1!= null || anno2 != null) {
                return (anno);
            }
        } catch (SecurityException e) {
            LOG.error("[BaseQueryPredicateBuilder.getBindAttrName SecurityException:]"
                    + e.getMessage());
        } catch (NoSuchFieldException e) {
            return null;
        }
        return null;
    }
}
